package service

import (
	"bytes"
	"encoding/json"
	baseLog "gitee.com/zaiqiang231/go-base-app/base_app/log"
	"gitee.com/zaiqiang231/lovesport-user-service/app/config"
	"gitee.com/zaiqiang231/lovesport-user-service/app/model"
	"gitee.com/zaiqiang231/lovesport-user-service/app/model/req"
	"gitee.com/zaiqiang231/lovesport-user-service/app/store"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"io"
	"net/url"
	"strings"
	"time"
)

type ServiceOperationRecord struct{}

var ServiceOperationRecordInstance = new(ServiceOperationRecord)

func OperationRecordFunc(c *gin.Context) {

	writer := responseBodyWriter{
		ResponseWriter: c.Writer,
		body:           &bytes.Buffer{},
	}
	c.Writer = writer

	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		baseLog.Errorf("read body from request error:", err)
	} else {
		c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
	}

	now := time.Now()
	c.Next()
	latency := time.Since(now)

	username := c.GetString(config.AuthIdentityKey)
	query := c.Request.URL.RawQuery
	query, _ = url.QueryUnescape(query)
	split := strings.Split(query, "&")
	m := make(map[string]string)
	for _, v := range split {
		kv := strings.Split(v, "=")
		if len(kv) == 2 {
			m[kv[0]] = kv[1]
		}
	}

	queryByte, _ := json.Marshal(&m)
	record := model.SysOperationRecord{
		Ip:           c.ClientIP(),
		Method:       c.Request.Method,
		Path:         c.Request.URL.Path,
		Agent:        c.Request.UserAgent(),
		UserName:     username,
		Body:         string(body),
		Query:        string(queryByte),
		Latency:      latency,
		Status:       c.Writer.Status(),
		ErrorMessage: c.Errors.ByType(gin.ErrorTypePrivate).String(),
		Resp:         writer.body.String(),
	}

	_ = ServiceOperationRecordInstance.CreateRecord(record)
}

type responseBodyWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (r responseBodyWriter) Write(b []byte) (int, error) {
	r.body.Write(b)
	return r.ResponseWriter.Write(b)
}

func (service *ServiceOperationRecord) CreateRecord(record model.SysOperationRecord) (err error) {
	store.GetDataStore().OperationRecord().Execute(func(db *gorm.DB) {
		err = db.Create(&record).Error
	})
	return
}

func (service *ServiceOperationRecord) GetList(info req.SearchOperationRecordReq) (list interface{}, total int64, err error) {
	store.GetDataStore().OperationRecord().Execute(func(db *gorm.DB) {
		limit := info.PageSize
		offset := info.PageSize * (info.Page - 1)
		var tempDb = db
		if info.Method != "" {
			tempDb = tempDb.Where("method = ?", info.Method)
		}
		if info.Path != "" {
			tempDb = tempDb.Where("path LIKE ?", "%"+info.Path+"%")
		}
		if info.Status != 0 {
			tempDb = tempDb.Where("status = ?", info.Status)
		}
		var tmpList []model.SysOperationRecord
		err = tempDb.Model(&model.SysOperationRecord{}).Count(&total).Error
		if err != nil {
			return
		}
		err = tempDb.Model(&model.SysOperationRecord{}).Offset(offset).Limit(limit).Preload("User", expandUser).Find(&tmpList).Error
		if err != nil {
			return
		}
		list = tmpList
	})
	return
}

func (service *ServiceOperationRecord) DeleteList(ids req.IdsReq) (err error) {
	store.GetDataStore().OperationRecord().Execute(func(db *gorm.DB) {
		var target []model.SysOperationRecord
		err = db.Find(&target, "id in ?", ids.Ids).Delete(&target).Error
	})
	return
}

func expandUser(db *gorm.DB) *gorm.DB {
	return db.Preload("Authority")
}
